
const { ccclass, menu, property } = cc._decorator;

@ccclass('Vertex')
export class Vertex {
    @property()
    private _position: cc.Vec2 = new cc.Vec2(0, 0);
    @property()
    public get position(): cc.Vec2 {
        return this._position;
    }
    public set position(value: cc.Vec2) {
        this._position = value;
    }

    @property()
    private _uv: cc.Vec2 = new cc.Vec2(0, 0);
    @property()
    public get uv(): cc.Vec2 {
        return this._uv;
    }
    public set uv(value: cc.Vec2) {
        this._uv = value;
    }

    // 指定的点，所属顶点索引 ==> vData
    @property()
    private _vertIndexList: number[] = [];
    @property()
    public get vertIndexList(): number[] {
        return this._vertIndexList;
    }

    // 指定的点，所属三角形索引信息 ==> iData
    @property()
    private _indiceIndexList: number[] = [];
    @property()
    public get indiceIndexList(): number[] {
        return this._indiceIndexList;
    }
}

@ccclass('Quad') 
export class Quad {
    @property()
    private _vertexLB: Vertex = new Vertex();
    @property()
    public get vertexLB(): Vertex {
        return this._vertexLB;
    }
    public set vertexLB(value: Vertex) {
        this._vertexLB = value;
    }

    @property()
    private _vertexRB: Vertex = new Vertex();
    @property()
    public get vertexRB(): Vertex {
        return this._vertexRB;
    }
    public set vertexRB(value: Vertex) {
        this._vertexRB = value;
    }

    @property()
    private _vertexLT: Vertex = new Vertex();
    @property()
    public get vertexLT(): Vertex {
        return this._vertexLT;
    }
    public set vertexLT(value: Vertex) {
        this._vertexLT = value;
    }

    @property()
    private _vertexRT: Vertex = new Vertex();
    @property()
    public get vertexRT(): Vertex {
        return this._vertexRT;
    }
    public set vertexRT(value: Vertex) {
        this._vertexRT = value;
    }
}

@ccclass('GridElement')
export class GridElement {

    // 行列索引
    @property()
    private _pointIndex: cc.Vec2 = new cc.Vec2(0, 0);
    @property()
    public get pointIndex(): cc.Vec2 {
        return this._pointIndex;
    }
    public set pointIndex(value: cc.Vec2) {
        this._pointIndex = value;
    }

    // 行列索引对应原始位置
    @property()
    private _originPosition: cc.Vec2 = new cc.Vec2(0, 0);
    @property()
    public get originPosition(): cc.Vec2 {
        return this._originPosition;
    }
    public set originPosition(value: cc.Vec2) {
        this._originPosition = value;
    }

    // 行列索引对应位置
    @property()
    private _position: cc.Vec2 = new cc.Vec2(0, 0);
    @property()
    public get position(): cc.Vec2 {
        return this._position;
    }
    public set position(value: cc.Vec2) {
        this._position = value;
    }

    // 行列索引对应渲染的四边形 (右上)
    @property()
    private _quad: Quad = new Quad();
    @property()
    public get quad(): Quad {
        return this._quad;
    }
    public set quad(value: Quad) {
        this._quad = value;
    }
}


/**
 * todo:
 * _isMask -> vertex
 * _isTile -> quad
 * _isCascade -> vertex
 * grid position list -> 面板
 * TexturePoly
 * Gizmo
 * GridElement -> cc.Node
 * front / back / zorder
 */

@ccclass
@menu('TextureGrid/TextureGrid')
export default class TextureGrid extends cc.Component {
    
    public stepX: number;
    public stepY: number;
    public uvStepX: number;
    public uvStepY: number;

    @property()
    private _isMask: boolean = false;
    public get isMask() {
        return this._isMask;
    }
    public set isMask(value) {
        this._isMask = value;
    }
    
    @property()
    private _isTile: boolean = false;
    public get isTile() {
        return this._isTile;
    }
    public set isTile(value) {
        this._isTile = value;
    }

    @property()
    private _isCascade: boolean = true;
    public get isCascade() {
        return this._isCascade;
    }
    public set isCascade(value) {
        this._isCascade = value;
    }

    @property()
    private _isDirty: boolean = true;
    public get isDirty() {
        return this._isDirty;
    }
    public set isDirty(value) {
        this._isDirty = value;
    }

    @property()
    private _xCount: number = 2;
    @property({ step: 1, min: 2, max: 20, tooltip: '上下边顶点数量(x 方向上)' })
    public get xCount() {
        return this._xCount;
    }
    public set xCount(value) {
        if (this._xCount === value) {
            return;
        }
        if (!this._xCount) {
            this._xCount = 2;
        }
        this._xCount = value;
    }

    @property()
    private _yCount: number = 2;
    @property({ step: 1, min: 2, max: 20, tooltip: '上下边顶点数量(x 方向上)' })
    public get yCount() {
        return this._yCount;
    }
    public set yCount(value) {
        if (this._yCount === value) {
            return;
        }
        if (!this._yCount) {
            this._yCount = 2;
        }
        this._yCount = value;
    }

    private grid: Array<Array<GridElement>> = null;

    onLoad() {
        this.initGrid()
        cc.log(this.grid)
    }

    initGrid() {
        this.stepX = this.node.width / (this.xCount);
        this.stepY = this.node.height / (this.yCount);

        this.uvStepX = 1.0 / (this.xCount);
        this.uvStepY = 1.0 / (this.yCount);
        
        this.grid = [];
        for (let i = 0; i <= this.xCount; i++) {
            this.grid[i] = [];
        }
        for (let i = 0; i <= this.xCount; i++) {
            for (let j = 0; j <= this.yCount; j++) {
                let grid = new GridElement();
                
                grid.pointIndex = cc.v2(i, j);
                grid.position = cc.v2(
                    -this.node.anchorX * this.node.width + i * this.stepX + this.node.x, 
                    -this.node.anchorY * this.node.height + j * this.stepY + this.node.y
                );
                grid.originPosition = grid.position.clone();
                this.updateQuad(grid);
                //
                this.grid[i][j] = grid;
            }
        }
    }

    updateQuad(gridElement) {
        this.updateVertexLB(gridElement);
        this.updateVertexRB(gridElement);
        this.updateVertexLT(gridElement);
        this.updateVertexRT(gridElement);
    }

    updateVertexLB(gridElement:GridElement, updateUV = true, offset = cc.v2(0, 0)) {
        gridElement.quad.vertexLB.position = cc.v2(gridElement.position.x, gridElement.position.y);
        if (updateUV) {
            gridElement.quad.vertexLB.uv = cc.v2(
                (gridElement.quad.vertexLB.position.x - this.node.position.x + this.node.width / 2) / this.node.width, 
                1.0 - (gridElement.quad.vertexLB.position.y - this.node.position.y + this.node.height / 2) / this.node.height, 
            );
        }
    }

    updateVertexRB(gridElement, updateUV = true, offset = cc.v2(0, 0)) {
        gridElement.quad.vertexRB.position = cc.v2(gridElement.quad.vertexLB.position.x + offset.x + this.stepX, gridElement.quad.vertexLB.position.y + offset.y);
        if (updateUV) {
            gridElement.quad.vertexRB.uv = cc.v2(gridElement.quad.vertexLB.uv.x + this.uvStepX, gridElement.quad.vertexLB.uv.y);
        }
    }

    updateVertexLT(gridElement, updateUV = true, offset = cc.v2(0, 0)) {
        gridElement.quad.vertexLT.position = cc.v2(gridElement.quad.vertexLB.position.x + offset.x, gridElement.quad.vertexLB.position.y + offset.y + this.stepY);
        if (updateUV) {
            gridElement.quad.vertexLT.uv = cc.v2(gridElement.quad.vertexLB.uv.x, gridElement.quad.vertexLB.uv.y - this.uvStepY);
        }
    }

    updateVertexRT(gridElement, updateUV = true, offset = cc.v2(0, 0)) {
        gridElement.quad.vertexRT.position = cc.v2(gridElement.quad.vertexLB.position.x + offset.x + this.stepX, gridElement.quad.vertexLB.position.y + offset.y + this.stepY);
        if (updateUV) {
            gridElement.quad.vertexRT.uv = cc.v2(gridElement.quad.vertexLB.uv.x + this.uvStepX, gridElement.quad.vertexLB.uv.y - this.uvStepY);
        }
    }

    restore() {
        for (let i = 0; i <= this.xCount; i++) {
            for (let j = 0; j <= this.yCount; j++) {
                let grid = this.grid[i][j];
                
                grid.pointIndex = cc.v2(i, j);
                grid.position = cc.v2(grid.originPosition.x, grid.originPosition.y);
                this.updateQuad(grid);
            }
        }
        this.isDirty = true;
    }

    getGrid() {
        return this.grid;
    }

    getGridAt(i, j) {
        if (this.grid[i]) {
            return this.grid[i][j];
        }
        else {
            return null;
        }
    }

    randomGrid() {
        return this.grid[Math.floor(Math.random() * this.xCount)][Math.floor(Math.random() * this.yCount)];
    }

    updateGrid(gridElement:GridElement) {
        this.setGridAt(gridElement.pointIndex.x, gridElement.pointIndex.y, gridElement);
    }

    setGridAt(i, j, gridElement:GridElement) {
        let deltaX = gridElement.position.x - gridElement.originPosition.x;
        let deltaY = gridElement.position.y - gridElement.originPosition.y;
        let offset = cc.v2(deltaX, deltaY);
        //
        this.updateVertexLB(gridElement, this.isMask, offset);
        if (this.isTile) {
            this.updateVertexRB(gridElement, this.isMask);
            this.updateVertexLT(gridElement, this.isMask);
            this.updateVertexRT(gridElement, this.isMask);
        }

        if (this.isCascade) {
            let gridLT = this.getGridAt(i - 1, j);
            if (gridLT) {
                this.updateVertexRB(gridLT, this.isMask, offset);
            }
            let gridLB = this.getGridAt(i - 1, j - 1);
            if (gridLB) {
                this.updateVertexRT(gridLB, this.isMask, offset);
            }
            let gridRB = this.getGridAt(i, j - 1);
            if (gridRB) {
                this.updateVertexLT(gridRB, this.isMask, offset);
            }
        }
        this.isDirty = true;
    }
}
